<?php


namespace app\common\lib;


use think\Db;
use think\db\exception\DataNotFoundException;
use think\db\exception\ModelNotFoundException;
use think\exception\DbException;

/**
 * 省市区县乡村,五级联动更新关闭状态
 */
trait SaveArea
{

    /**
     * 多个层级的map
     * @var string[]
     */
    protected $LevelTableName = [
        1 => 'province',
        2 => 'city',
        3 => 'area',
        4 => 'township',
        5 => 'village',
    ];

  
    /**
     *  获取某个省的所有文件数据
     */
    private function getProvinceFileContent($id)
    {
        // todo 这里可以继续 优化，放在redis 之类的内存中，减少io次数
        $file_path = ROOT_PATH . 'redundancy_area' . DIRECTORY_SEPARATOR . "city.{$id}.json";
        if (!file_exists($file_path)) {
            return [];
        }

        /** 文件名中的路径中包含第一级 id  ，省
         * json 文件内容，
         */
        $json_conetnt = [
            // 市
            '1'=>[
                // 区
                '2'=>[
                    // 乡/街道办
                    '3'=>[
                        // 村
                        '4'
                    ]
                ]
            ]
        ];


        $json_arr = json_decode(file_get_contents($file_path), true);
        return $json_arr;
    }

    /**
     * 更新状态的具体逻辑
     * @return \think\response\Json
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function saveAddStatus()
    {
        //要更新的那个地区id
        $post_data_id = input('post.id', 0);
        //当前是哪一级
        $level_val = input('post.table', '');
        $field_save_name = input('post.name', '');


        // $level_val = $this->getTablesWithLevel($table);
        //更新还是关闭
        $value = input('post.value', 0);
        // $value = 1;
        if (!$post_data_id || !$level_val) {
            $value = array('status' => 0, 'mess' => '参数错误');
            return json($value);
        }
        $shopId = session('shop_id');
        $shop_id = session('shopsh_id');


        $shop_id = $field_save_name == 'checked' ? 0 : $shopId ?? $shop_id;

        $info = Db::name($this->getLevelTableName($level_val))->where('id', $post_data_id)->find();

        if (empty($info)) {
            $value = array('status' => 0, 'mess' => "地区错误");
            return json($value);
        }
        $area_allow = Db::name('area_allow')->where('mer_id', $shop_id)->find();

        if (!$area_allow) {
            Db::name('area_allow')->insert(['mer_id' => $shop_id]);
        }

        $this->savesAllowIds($shop_id, $value, $level_val, $info);

        $value = array('status' => 1, 'mess' => "地区更新完毕");
        return json($value);
    }

    /**
     * 获取要放行的地区id
     * @return void
     */
    private function savesAllowIds($shop_id, $status, $level, $info)
    {
        $pro_id = $info['pro_id'] ?? $info['id'];
        //所有省的数据
        $tmp_spl_arr = $this->getProvinceFileContent($pro_id);

        $tmp = static::getredunData($tmp_spl_arr, $level, $info);

        $result = $this->extractKeysRecursively($tmp);
        //开启
        if ($status == 1) {
            $result = static::getOnArea($level, $info, $result);
            //开启每个上级 ，和下级
            $area_data = Db::name('area_allow')->where('mer_id', $shop_id)->find();
        } else {
            //关闭
            array_unshift($result, [$info['id']]);

            $area_data = Db::name('area_allow')->where('mer_id', $shop_id)->field($this->getAllowField($level))->find();
        }
        unset($area_data['id'], $area_data['mer_id']);
        //合并后去重
        $save_data = $this->RemoveDuplicatesWithSort($result, $area_data, $status);

        Db::name('area_allow')->where('mer_id', $shop_id)->update($save_data);

        return true;
    }

    /**
     * 去重并合并每个等级的id
     * @return void
     */
    private function RemoveDuplicatesWithSort($result, $area_data, $status)
    {
        $i = 0;
        $new_save_data = [];
        foreach ($area_data as $key => $value) {
            $value = empty($value) ? [] : explode(',', $value);

            //开启
            if ($status == 1) {
                $tmp_new_arr = array_unique(array_merge($value, $result[$i]));
            } else {
                $tmp_new_arr = array_diff($value, $result[$i]);
            }
            sort($tmp_new_arr, SORT_ASC);

            $new_save_data[$key] = implode(',', $tmp_new_arr);
            ++$i;
        }
        return $new_save_data;

    }

    /**
     * 获取指定等级的字段
     * @param $level
     * @return string[]|void
     */
    private function getAllowField($level)
    {

        if ($level == 1) {
            return ['pro_ids', 'city_ids', 'area_ids', 'township_ids', 'village_ids'];
        }

        if ($level == 2) {
            return ['city_ids', 'area_ids', 'township_ids', 'village_ids'];
        }


        if ($level == 3) {
            return ['area_ids', 'township_ids', 'village_ids'];
        }


        if ($level == 4) {
            return ['township_ids', 'village_ids'];
        }


        if ($level == 5) {
            return ['village_ids'];
        }
    }


    /**
     * 获取指定等级的地区的数据
     * @param $tmp_result
     * @param $level
     * @param $info
     * @return array|void
     */
    protected static function getredunData($tmp_result, $level, $info)
    {
        //所有市
        if ($level == 1) {
            return $tmp_result;
        }

        //所有县/区
        if ($level == 2) {
            return $tmp_result[$info['city_id']];
        }
        // 所有 乡镇
        if ($level == 3) {
            return $tmp_result[$info['city_id']][$info['id']];
        }
        //所有村
        if ($level == 4) {
            return $tmp_result[$info['city_id']][$info['area_id']][$info['id']];
            //
        }
        //一个村
        if ($level == 5) {
            return [$info['id'] => ""];
        }

    }


    /**
     * 递归获取每个等级的ids
     * @param $obj
     * @param $level
     * @param $result
     * @return array|mixed
     */
    private function extractKeysRecursively($obj, $level = 0, &$result = [])
    {

        foreach ($obj as $key => $value) {
            if ($value == "") {
                $result["$level"][] = $key;
            } else {
                $result["$level"][] = $key;
                $this->extractKeysRecursively($value, $level + 1, $result);
            }
        }
        return $result;
    }

    /**
     * 获取开启的上级的id
     * @return void
     */
    private static function getOnArea($level, $info, $result)
    {

        switch ($level) {
            case 1:
                array_unshift($result, [$info['id']]);
                break;
            case 2:
                array_unshift($result, [$info['pro_id']], [$info['id']]);
                break;
            case 3:
                array_unshift($result, [$info['pro_id']], [$info['city_id']], [$info['id']]);
                break;
            case 4:
                array_unshift($result, [$info['pro_id']], [$info['city_id']], [$info['area_id']], [$info['id']]);
                break;
            case 5:
                array_unshift($result, [$info['pro_id']], [$info['city_id']], [$info['area_id']], [$info['country_id']]);
                break;
        }
        return $result;
    }

    /**
     * 获取指定商户的放行地区
     * @param $shop_id
     * @return array|bool|\PDOStatement|string|\think\Model|null
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function getMerAreaAllow($shop_id)
    {
        return Db::name('area_allow')->where('mer_id', $shop_id)->find() ?? [];
    }

    public function getMerAreaAllowWithLevel($shop_id, $field)
    {
        $info = Db::name('area_allow')
            ->where(function ($query) use ($shop_id) {
                if ($shop_id == 0) {
                    $query->where('mer_id', 1);
                } else {
                    $query->where('mer_id', $shop_id);
                }
            })
            ->field(['id', $field])
            ->find();
        if (!$info) {
            return ['id' => 0, $field => ''];
        }
        return ['id' => $info['id'], $field => $info[$field]];
    }

    /**
     * 检索指定id 是否存在于指定数组中，如果存在返回1，不存在返回0，使用二分查找算法
     * @param $info
     * @param $id
     * @return int
     */
    protected function getSelfCheckStatus($info, $id)
    {
        if (empty($info)) {
            return 0;
        }
        // 生成定长数组，以减少内存消耗
        $splArray = \SplFixedArray::fromArray(explode(',', $info));

        if ($this->binarySearch($splArray->toArray(), (string)$id) == -1) {
            return 0;
        } else {
            return 1;
        }
    }

    /**
     * 二分查找算法
     * @param $arr
     * @param $target
     * @return false|float|int
     */
    protected function binarySearch($arr, $target)
    {
        $left = 0;
        $right = count($arr) - 1;

        while ($left <= $right) {
            $mid = floor(($left + $right) / 2);
            if ($arr[$mid] === $target) {
                return $mid;
            } elseif ($arr[$mid] < $target) {
                $left = $mid + 1;
            } else {
                $right = $mid - 1;
            }
        }

        return -1; // 目标元素不存在于数组中
    }

    /**
     * 获取指定层级的地址
     * @return void
     */
    public function getAreaTree()
    {
        $pid = $this->request->get('pid', 0);
        $level = $this->request->get('level', 1);
        $field = [];
        $where = [];
        $area_allow_field = '';
        switch ($level) {
            case 1:
                $field = ['id', 'pro_name AS title'];
                $area_allow_field = 'pro_ids';
                break;
            case 2:
                $field = ['id', 'city_name AS title'];
                $where['pro_id'] = $pid;
                $area_allow_field = 'city_ids';

                break;
            case 3:
                $field = ['id', 'area_name AS title'];
                $where['city_id'] = $pid;
                $area_allow_field = 'area_ids';

                break;
            case 4:
                $field = ['id', 'country_name AS title'];
                $where['area_id'] = $pid;
                $area_allow_field = 'township_ids';

                break;
            case 5:
                $field = ['id', 'village_name AS title'];
                $where['country_id'] = $pid;
                $area_allow_field = 'village_ids';
                break;
        }

        $info = Db::name($this->getLevelTableName($level))
            ->field($field)
            ->where(function (\think\Db\Query $query) use ($where) {
                if ($where) {
                    $query->where($where);
                }
            })
            ->select();
        $shop_id = session('shopsh_id') ?? session('shop_id');
        $area_allow = $this->getMerAreaAllowWithLevel($shop_id, $area_allow_field);
        $v1_area_allow = [];
        if ($shop_id == 1) {
            $v1_area_allow = $this->getMerAreaAllowWithLevel(0, $area_allow_field);
        }

        $jump_level = $level + 1;
        $module = request()->module();
        $domian = request()->domain();
        foreach ($info as &$value) {
            //自营地区开启状态
            $value['self_checked'] = empty($area_allow[$area_allow_field]) ? 0 : $this->getSelfCheckStatus($area_allow[$area_allow_field], $value['id']);
            $value['jump_url'] = $domian . '/' . $module . '/Province/lst?level=' . $jump_level . '&pid=' . $value['id'];
            if ($shop_id == 1) {
                //平台申请地区
                $value['checked'] = empty($v1_area_allow[$area_allow_field]) ? 0 : $this->getSelfCheckStatus($v1_area_allow[$area_allow_field], $value['id']);
            }
        }
        unset($value);
        $this->assign('list', $info);
        $this->assign('level', $level);
        $this->assign('jump_level', $jump_level);

        $this->assign('crumbs', $this->getCrumbs($level, $pid));

        return $this->fetch('province/lstV1');
    }

    /**
     * 获取当前地区的上级导航栏
     * @param $level
     * @param $id
     * @return void
     */
    public function getCrumbs($level, $id)
    {
        if ($level == 1) {
            return '';
        }

        $field = '';
        $WHERE = '1=1 AND ';
        $join_sql = '';
        if ($level >= 2) {
            $join_sql = 'INNER JOIN sp_province AS sp ON base_t.pro_id = sp.id ';
            $field .= 'sp.pro_name ,sp.id AS v1_id';
            $WHERE = "base_t.pro_id=$id ";
        }
        if ($level >= 3) {
            $join_sql .= ' INNER JOIN sp_city AS sc ON base_t.city_id = sc.id ';
            $field .= ',sc.city_name ,sc.id AS v2_id';
            $WHERE = "base_t.city_id=$id ";
        }

        if ($level >= 4) {
            $join_sql .= ' INNER JOIN sp_area AS sa ON base_t.area_id = sa.id ';
            $field .= ',sa.area_name ,sa.id AS v3_id';
            $WHERE = "base_t.area_id=$id ";
        }

        if ($level >= 5) {
            $join_sql .= ' INNER JOIN sp_township AS st ON base_t.country_id = st.id ';
            $field .= ',st.country_name ,st.id AS v4_id';
            $WHERE = "base_t.country_id=$id ";
        }


        $table_name = 'sp_' . $this->getLevelTableName($level);
        $sql = "SELECT $field FROM $table_name AS base_t  $join_sql WHERE {$WHERE}";
        $info = Db::query($sql);

        if (empty($info)) {
            return '';
        }
        $info = current($info);
        $show_crumbs = '';


        $domian = request()->domain() . '/' . request()->module();

        if (isset($info['pro_name']) && isset($info['v1_id'])) {
            $url = $domian . '/Province/lst';
            $show_crumbs .= "<a href='" . $url . "' >{$info['pro_name']}</a>";
        }

        if (isset($info['city_name']) && isset($info['v2_id'])) {
            $url = $domian . '/Province/lst?level=2&pid=' . $info['v1_id'];
            $show_crumbs .= "><a href='" . $url . "' >{$info['city_name']}</a>";
        }
        if (isset($info['area_name']) && isset($info['v3_id'])) {
            $url = $domian . '/Province/lst?level=3&pid=' . $info['v2_id'];
            $show_crumbs .= "><a href='" . $url . "' >{$info['area_name']}</a>";
        }

        if (isset($info['country_name']) && isset($info['v4_id'])) {
            $url = $domian . '/Province/lst?level=4&pid=' . $info['v3_id'];
            $show_crumbs .= "><a href='" . $url . "' >{$info['country_name']}</a>";
        }
        return $show_crumbs;
    }


    /**
     * 获取指定层级的表名称
     * @param $level
     * @return string
     */
    protected function getLevelTableName($level)
    {
        if (!isset($this->LevelTableName[$level])) {
            throw new \RuntimeException('不存在的层级');
        }
        return $this->LevelTableName[$level];
    }


    /**
     * 获取指定层级的地区 然后从数据库
     * @param $id  上级id
     * @param $level  上级id 等级
     * @param $show_level  当前要显示的等级
     * @return void
     * @throws DataNotFoundException
     * @throws ModelNotFoundException
     * @throws DbException
     */
    protected function diffAreaIds($field, $id, $level, $show_level)
    {
        $p_info = Db::name($this->getLevelTableName($level))
            ->where($field, $id)
            ->find();

        $tmp_spl_arr = $this->getProvinceFileContent($p_info['pro_id']);
        //得到当前等级的 所有地区id
        $tmp = static::getredunData($tmp_spl_arr, $show_level, $p_info);

        $tmp = array_keys($tmp);

        $field = '';
        switch ($show_level) {
            case  1:
                $field = 'city_ids';
                break;
            case  2:
                $field = 'area_ids';
                break;
            case  3:
                $field = 'township_ids';
                break;
            case  4:
                $field = 'village_ids';
                break;
        }
        $allow_area_config = $this->getMerAreaAllowWithLevel(1, $field);

        if (empty($allow_area_config) || !isset($allow_area_config[$field])) {
            return [];
        }

        $result = array_intersect($tmp, explode(',', $allow_area_config[$field]));
        return $result;
    }

}
